<script lang="ts" setup>
import dayjs from "dayjs";
import type { DrawerProps } from "element-plus";

// 类型声明
type GenericObject = {
  [key: string]: any;
};
type RecordObject = Record<string, any>;

const emit = defineEmits<{
  handle_add: [];
  handle_edit: [row: GenericObject];
  getList_done: [data: RecordObject[], total: number];
}>();

// 父组件传参
const { Model, PageConfig, tableDataFormate } = defineProps<{
  Model: {
    [key: string]: GenericObject;
  };
  PageConfig: GenericObject;
  tableDataFormate?: (row: RecordObject) => RecordObject;
}>();

// 父组件传参--二次加工
const propData = computed(() => {
  let searchForm_formItemConfigList: GenericObject[] = [];
  let form_customFormItemConfigList: GenericObject[] = [];
  let defaultFormData: RecordObject = {};
  let table_columnConfigList: GenericObject[] = [];
  let top4_searchFieldList: string[] = [];

  for (const [key, value] of Object.entries(Model)) {
    if ("search" in value && value.search) {
      searchForm_formItemConfigList.push({
        prop: key,
        ...(value as object),
        searchOrder: value.searchOrder || 99,
      });
    }

    if (value.type === "custom") {
      form_customFormItemConfigList.push({
        prop: key,
        ...(value as object),
      });
    }

    if (value.defaultValue) {
      defaultFormData[key] = value.defaultValue;
    }

    if (!value.tableHide) {
      table_columnConfigList.push({
        prop: key,
        ...(value as object),
        tableOrder: value.tableOrder || 999,
      });
    }
  }
  searchForm_formItemConfigList = searchForm_formItemConfigList.sort(
    (a, b) => a.searchOrder - b.searchOrder
  );

  searchForm_formItemConfigList.forEach((item, index) => {
    if (index < 4) {
      top4_searchFieldList.push(item.prop);
      if (item.type === "dateRange") {
        top4_searchFieldList.push(item.prop + "_start");
        top4_searchFieldList.push(item.prop + "_end");
      }
    }
  });

  table_columnConfigList = table_columnConfigList.sort(
    (a, b) => a.tableOrder - b.tableOrder
  );

  return {
    searchForm_formItemConfigList,
    form_customFormItemConfigList,
    defaultFormData,
    table_columnConfigList,
    top4_searchFieldList,
  };
});

const direction = ref<DrawerProps["direction"]>("rtl");

const show_allSearch = ref(false);

const callbackMessage = ref({
  show: false,
  valid: true,
  content: "",
});

// 当前操作状态
const action = ref("search");

// 操作状态字典
const actionDic: GenericObject = {
  add: "新增",
  edit: "修改",
  detail: "详情",
};

const pageData = reactive<{
  currentPage: number;
  pageSize: number;
  total: number;
  searchformData: RecordObject;
  tableData: RecordObject[];
  loadData: boolean;
}>({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  searchformData: {},
  tableData: [],
  loadData: false,
});

const { currentPage, pageSize, total, searchformData, tableData, loadData } =
  toRefs(pageData);

// 排序配置
const sort = ref({
  column: "createdAt",
  direction: "desc",
});

// 日期选择组件配置
const shortcuts = [
  {
    text: "上周",
    value: () => {
      const end = new Date();
      const start = new Date();
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
      return [start, end];
    },
  },
  {
    text: "上个月",
    value: () => {
      const end = new Date();
      const start = new Date();
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
      return [start, end];
    },
  },
  {
    text: "三个月前",
    value: () => {
      const end = new Date();
      const start = new Date();
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
      return [start, end];
    },
  },
];

// 搜索表单宽度样式
const formItemWidthClass = "w-260px!";

// 编辑表单数据
let formData: GenericObject = {};

// 是否添加了更多搜索条件
const hasMoreSearchCondition = computed(() => {
  let result = false;
  for (const key in pageData.searchformData) {
    if (
      !propData.value.top4_searchFieldList.includes(key) &&
      (pageData.searchformData[key] || pageData.searchformData[key] === 0)
    ) {
      result = true;
      break;
    }
  }
  return result;
});

// 自适配保存的API
const saveAPI = computed(() => {
  if (action.value === "add") {
    if (PageConfig.api && PageConfig.api.add) {
      return PageConfig.api.add;
    } else {
      return `/${PageConfig.entity}/add`;
    }
  } else {
    if (PageConfig.api && PageConfig.api.edit) {
      return PageConfig.api.edit;
    } else {
      return `/${PageConfig.entity}/edit`;
    }
  }
});

// 获取分页表格数据
const getList = async () => {
  loadData.value = true;
  let searchformData_temp: RecordObject = JSON.parse(
    JSON.stringify(pageData.searchformData)
  );
  for (const key in searchformData_temp) {
    if (typeof searchformData_temp[key] === "object") {
      delete searchformData_temp[key];
    }
  }
  const res: GenericObject = await $fetch(`/api/${PageConfig.entity}/list`, {
    query: {
      orderBy: sort.value.column,
      order: sort.value.direction,
      currentPage: pageData.currentPage,
      pageSize: pageData.pageSize,
      ...searchformData_temp,
    },
  });
  if (res) {
    tableData.value = res.data.map((row: RecordObject) => {
      row.createdAt = dayjs(row!.createdAt).format("YYYY-MM-DD HH:mm");
      row.updatedAt = dayjs(row!.updatedAt).format("YYYY-MM-DD HH:mm");
      return tableDataFormate ? tableDataFormate(row) : row;
    });
    total.value = res.total;
  }
  loadData.value = false;
  emit("getList_done", tableData.value, total.value);
};

// 按钮--取消（多条件搜索抽屉中）
function cancelMoreSearch() {
  show_allSearch.value = false;
}

// 按钮--查询（多条件搜索抽屉中）
function confirmMoreSearch() {
  getList();
  show_allSearch.value = false;
}

// 切换分页-每页条数
const handleSizeChange = (val: number) => {
  pageSize.value = val;
  getList();
};

// 点击分页-页码
const handleCurrentChange = (val: number) => {
  currentPage.value = val;
  getList();
};

// 点击按钮-重置
const reset = () => {
  pageData.currentPage = 1;
  pageData.pageSize = 10;
  pageData.searchformData = {};
  getList();
};

// 点击按钮-新增
const handle_add = () => {
  formData = JSON.parse(JSON.stringify(propData.value.defaultFormData));
  action.value = "add";
  emit("handle_add");
};

// 点击按钮-编辑
const handle_edit = (row: GenericObject) => {
  formData = row;
  action.value = "edit";
  emit("handle_edit", row);
};

// 点击按钮-删除
const handle_del = async (id: string) => {
  try {
    await useFetch(`/api/${PageConfig.entity}/${id}`, {
      method: "DELETE",
    });
    callbackMessage.value = {
      show: true,
      valid: true,
      content: "操作成功",
    };
    getList();
  } catch (e: any) {
    callbackMessage.value = {
      show: true,
      valid: false,
      content: e.data.message,
    };
  }
};

// 点击按钮-重置密码
const handle_resetPassword = async (id: string) => {
  try {
    await useFetch(`/api/user/${id}`, {
      method: "PATCH",
      query: {
        handleType: "resetPassword",
      },
    });
    callbackMessage.value = {
      show: true,
      valid: true,
      content: "操作成功",
    };
  } catch (e: any) {
    callbackMessage.value = {
      show: true,
      valid: false,
      content: e.data.message,
    };
  }
};

// 点击按钮-详情
const handle_detail = (row: GenericObject) => {
  formData = row;
  action.value = "detail";
};

// 回调函数--保存成功
const saveOK = async () => {
  setTimeout(() => {
    action.value = "search";
    getList();
  }, 500);
};

const dateRangeChange = (newValue: string[] | null, prop: string) => {
  if (newValue) {
    pageData.searchformData[prop + "_start"] = newValue[0];
    pageData.searchformData[prop + "_end"] = newValue[1];
  } else {
    delete pageData.searchformData[prop + "_start"];
    delete pageData.searchformData[prop + "_end"];
  }
};

// 处理 before-change 事件的方法
const beforeSwitchChange = async (
  oldValue: boolean | number | string,
  prop: string
) => {
  if (PageConfig.entity === "user" && prop === "disabled") {
    try {
      // 弹出确认框
      await ElMessageBox.confirm(
        `确定${oldValue ? "启用" : "禁用"}吗?`,
        "提示",
        {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        }
      );
      // 用户点击了确定，允许状态改变
      return true;
    } catch (error) {
      // 用户点击了取消，阻止状态改变
      return false;
    }
  } else {
    return true;
  }
};

const switchChange = async (
  newValue: boolean | number | string,
  id: string
) => {
  try {
    await useFetch(`/api/user/${id}`, {
      method: "PATCH",
      query: {
        handleType: newValue ? "disable" : "enable",
      },
    });
    callbackMessage.value = {
      show: true,
      valid: true,
      content: "操作成功",
    };
  } catch (e: any) {
    callbackMessage.value = {
      show: true,
      valid: false,
      content: e.data.message,
    };
  }
};

onMounted(() => {
  getList();
});

// 对外暴露的变量和方法
defineExpose({
  saveOK,
  getList,
});
</script>
<template>
  <div class="relative">
    <!-- 过渡动画--从右向左滑入 -->
    <transition name="slide-in">
      <div class="bg-white" v-if="['add', 'edit', 'detail'].includes(action)">
        <el-page-header @back="action = 'search'">
          <template #content>
            <span class="text-large font-600 mr-3">
              <span v-if="actionDic[action] !== `详情`">{{
                actionDic[action]
              }}</span>
              <span>{{ PageConfig.entityName }}</span>
              <span v-if="actionDic[action] === `详情`">{{
                actionDic[action]
              }}</span>
            </span>
          </template>
        </el-page-header>
        <S-form
          :Model="Model"
          class="m-4"
          :PageConfig="PageConfig"
          :action="action"
          :disabled="action === 'detail'"
          :cancel="
            () => {
              action = 'search';
            }
          "
          :local_save="PageConfig.local_save"
          :saveOK="saveOK"
          :saveAPI="saveAPI"
          v-model="formData"
        >
          <template
            v-for="formItemConfig in propData.form_customFormItemConfigList"
            :key="formItemConfig.prop"
            #[formItemConfig.prop]
          >
            <slot :name="formItemConfig.prop" />
          </template>
        </S-form>
      </div>
    </transition>
    <!-- 过渡动画--向中心缩小消失 -->
    <transition name="shrink-to-center">
      <div v-show="action === 'search'">
        <!-- 搜索表单 -->
        <el-form class="mt-2" :inline="true" :model="searchformData">
          <el-row :span="24">
            <template
              v-for="(
                formItemConfig, index
              ) in propData.searchForm_formItemConfigList"
            >
              <el-col
                v-if="!formItemConfig.tableHide && index < 4"
                :span="12"
                :key="formItemConfig.prop"
                class="text-center even:ml-[-60px]!"
              >
                <el-form-item :label="formItemConfig.label" :label-width="160">
                  <el-date-picker
                    v-if="formItemConfig.type === 'date'"
                    v-model="searchformData![formItemConfig.prop as string]"
                    type="date"
                    placeholder="选择日期"
                    v-bind="formItemConfig"
                    :class="formItemWidthClass"
                  />
                  <el-date-picker
                    v-else-if="formItemConfig.type === 'dateRange'"
                    v-model="searchformData![formItemConfig.prop as string]"
                    type="daterange"
                    unlink-panels
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    :shortcuts="shortcuts"
                    value-format="YYYY-MM-DD"
                    @change="dateRangeChange($event, formItemConfig.prop)"
                    :class="formItemWidthClass"
                  />
                  <el-date-picker
                    v-else-if="formItemConfig.type === 'dateTimeRange'"
                    v-model="searchformData![formItemConfig.prop as string]"
                    type="datetimerange"
                    unlink-panels
                    range-separator="至"
                    start-placeholder="开始时间"
                    end-placeholder="结束时间"
                    :shortcuts="shortcuts"
                    value-format="YYYY-MM-DD"
                    @change="dateRangeChange($event, formItemConfig.prop)"
                    :class="formItemWidthClass"
                  />
                  <el-tree-select
                    v-else-if="formItemConfig.type === 'treeSelect'"
                    v-model="searchformData![formItemConfig.prop as string]"
                    :data="formItemConfig.treeData"
                    :render-after-expand="false"
                    placeholder="请选择"
                    :class="formItemWidthClass"
                    filterable
                    clearable
                    :node-key="formItemConfig.key"
                    default-expand-all
                  />
                  <el-input
                    v-else
                    v-model="searchformData![formItemConfig.prop as string]"
                    v-bind="formItemConfig"
                    :class="formItemWidthClass"
                    clearable
                  />
                </el-form-item>
              </el-col>
            </template>
          </el-row>
        </el-form>
        <!-- 查询按钮组 -->
        <div class="flex justify-center relative">
          <el-button type="primary" @click="getList">查询</el-button>
          <el-button type="primary" @click="reset">重置</el-button>
          <el-button
            v-if="propData.searchForm_formItemConfigList.length > 4"
            class="absolute right-4"
            type="primary"
            text
            @click="show_allSearch = true"
          >
            <span class="text-red" v-if="hasMoreSearchCondition"
              >（已添加）</span
            >
            更多搜索条件 >></el-button
          >
        </div>
        <!-- 全部搜索条件抽屉 -->
        <el-drawer v-model="show_allSearch" :direction="direction">
          <template #header>
            <h4 class="font-bold">全部搜索条件</h4>
          </template>
          <template #default>
            <!-- 搜索表单 -->
            <el-form :inline="true" :model="searchformData">
              <el-row :span="24">
                <template
                  v-for="formItemConfig in propData.searchForm_formItemConfigList"
                >
                  <el-col
                    v-if="!formItemConfig.tableHide"
                    :span="24"
                    :key="formItemConfig.prop"
                  >
                    <el-form-item
                      :label="formItemConfig.label"
                      :label-width="100"
                    >
                      <el-date-picker
                        v-if="formItemConfig.type === 'date'"
                        v-model="searchformData![formItemConfig.prop as string]"
                        type="date"
                        placeholder="选择日期"
                        v-bind="formItemConfig"
                        :class="formItemWidthClass"
                      />
                      <el-date-picker
                        v-else-if="formItemConfig.type === 'dateRange'"
                        v-model="searchformData![formItemConfig.prop as string]"
                        type="daterange"
                        unlink-panels
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        :shortcuts="shortcuts"
                        value-format="YYYY-MM-DD"
                        @change="dateRangeChange($event, formItemConfig.prop)"
                        :class="formItemWidthClass"
                      />
                      <el-tree-select
                        v-else-if="formItemConfig.type === 'treeSelect'"
                        v-model="searchformData![formItemConfig.prop as string]"
                        :data="formItemConfig.treeData"
                        :render-after-expand="false"
                        placeholder="请选择"
                        :class="formItemWidthClass"
                        filterable
                        clearable
                        :node-key="formItemConfig.key"
                        default-expand-all
                      />
                      <el-input
                        v-else
                        v-model="searchformData![formItemConfig.prop as string]"
                        v-bind="formItemConfig"
                        :class="formItemWidthClass"
                        clearable
                      />
                    </el-form-item>
                  </el-col>
                </template>
              </el-row>
            </el-form>
          </template>
          <template #footer>
            <div style="flex: auto">
              <el-button @click="cancelMoreSearch">取消</el-button>
              <el-button type="primary" @click="reset">重置</el-button>
              <el-button type="primary" @click="confirmMoreSearch"
                >查询</el-button
              >
            </div>
          </template>
        </el-drawer>
        <!-- 新增按钮 -->
        <div class="m-2 mb-4 flex">
          <el-button
            v-if="!PageConfig.disAdd"
            v-permission="`${PageConfig.entity}:add`"
            type="primary"
            @click="handle_add"
            >新增</el-button
          >
        </div>
        <!-- 表格 -->
        <el-table
          height="360"
          :data="tableData"
          style="width: 96%"
          empty-text="暂无数据"
          v-loading="loadData"
        >
          <el-table-column
            v-for="tableColumnConfig in propData.table_columnConfigList"
            :key="tableColumnConfig.prop"
            v-bind="tableColumnConfig"
            :align="tableColumnConfig.align || 'center'"
            show-overflow-tooltip
          >
            <template #default="scope">
              <span v-if="tableColumnConfig.tableTransProp">
                {{ scope.row[tableColumnConfig.tableTransProp] }}
              </span>
              <el-switch
                v-if="tableColumnConfig.type === 'switch'"
                v-model="scope.row[tableColumnConfig.prop]"
                :before-change="
                  () =>
                    beforeSwitchChange(
                      scope.row[tableColumnConfig.prop],
                      tableColumnConfig.prop
                    )
                "
                @change="switchChange($event, scope.row._id)"
              />

              <slot
                v-if="tableColumnConfig.type === 'custom'"
                :name="`table_${tableColumnConfig.prop}`"
                :row="scope.row"
              ></slot>
            </template>
          </el-table-column>

          <el-table-column
            v-if="!PageConfig.hideHandle"
            fixed="right"
            label="操作"
            min-width="120"
            align="center"
          >
            <template #default="scope">
              <el-button
                v-if="
                  !PageConfig.hideDetailBtn &&
                  !(
                    PageConfig.entity === 'role' &&
                    scope.row.name === '超级管理员'
                  )
                "
                link
                type="primary"
                size="small"
                @click="handle_detail(scope.row)"
                v-permission="`${PageConfig.entity}:detail`"
                >详情</el-button
              >
              <el-button
                v-if="
                  !PageConfig.hideEditBtn &&
                  !(
                    PageConfig.entity === 'role' &&
                    scope.row.name === '超级管理员'
                  )
                "
                link
                type="primary"
                size="small"
                @click="handle_edit(scope.row)"
                v-permission="`${PageConfig.entity}:edit`"
                >编辑</el-button
              >
              <el-popconfirm
                title="确定删除吗?"
                @confirm="handle_del(scope.row._id)"
              >
                <template #reference>
                  <el-button
                    v-if="
                      !PageConfig.hideDelBtn &&
                      !(
                        PageConfig.entity === 'role' &&
                        scope.row.name === '超级管理员'
                      )
                    "
                    link
                    type="danger"
                    size="small"
                    v-permission="`${PageConfig.entity}:del`"
                    >删除</el-button
                  >
                </template>
              </el-popconfirm>
              <el-popconfirm
                v-if="PageConfig.showResetPasswordBtn"
                title="确定重置吗?"
                @confirm="handle_resetPassword(scope.row._id)"
              >
                <template #reference>
                  <el-button
                    link
                    type="danger"
                    size="small"
                    v-permission="`${PageConfig.entity}:resetPassword`"
                    >重置密码</el-button
                  >
                </template>
              </el-popconfirm>
              <slot name="myHandle" :row="scope.row" />
            </template>
          </el-table-column>
        </el-table>
        <!-- 分页 -->
        <div class="mt-4 flex justify-end">
          <el-pagination
            v-model:current-page="currentPage"
            v-model:page-size="pageSize"
            :page-sizes="[5, 10, 20, 30]"
            layout="total, sizes, prev, pager, next, jumper"
            :total="total"
            size="small"
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
          />
        </div>
      </div>
    </transition>
    <S-msgWin :msg="callbackMessage" />
  </div>
</template>
<style scoped>
/* 过渡动画 -- 从右向左滑入 */
.slide-in-enter-active {
  animation: slideIn 0.5s ease-out;
}
@keyframes slideIn {
  0% {
    transform: translateX(100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1;
  }
}
/* 过渡动画 -- 向中心缩小消失 */
.shrink-to-center-enter-active {
  animation: shrinkToCenter 0.5s reverse;
}
.shrink-to-center-leave-active {
  animation: shrinkToCenter 0.5s;
}
@keyframes shrinkToCenter {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(0);
    opacity: 0;
  }
}
</style>
